package com.proxy.server.backend.nio;

import com.proxy.common.packet.BinaryPacket;
import com.proxy.server.backend.Trigger;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * mysql异步接收器
 * Created by liufish on 16/12/21.
 */
public class AsyncReceiver {

    private static final int status_init_and_head = 0;
    private static final int status_fields_eof = 1;
    private static final int status_rows_eof = 2;
    private volatile int status = status_init_and_head;

    private volatile BinaryPacket head;
    private volatile List<BinaryPacket> fields;

    private volatile Trigger trigger;

    private Lock lock = new ReentrantLock();

    private NioBackendConnection connection;

    public AsyncReceiver(NioBackendConnection connection){
        this.connection = connection;
    }

    public void receive(BinaryPacket bin){
        /**
         * 初始化处理,成功、失败、文件、头
         */
        if(status == status_init_and_head){
            switch (bin.body[0]){
                case (byte)0x00:
                    //成功结束
                    this.triggerSuccess( bin);
                    break;
                case (byte)0xff:
                    //失败结束
                    this.triggerError(bin);
                    break;
                case (byte) 251:
                    //文件,暂时不支持
                    //结束
                    status = status_init_and_head;
                    break;
                default:
                    initFieldsSize(bin);
                    break;
            }
            return;
        }
        /**
         * 处理头以下的field 和 eof 信息
         */
        if(status == status_fields_eof){
            switch (bin.body[0]){
                case (byte) 0xff:
                    //失败结束
                    this.triggerError(bin);
                    break;
                case (byte)0xfe:
                    this.triggerHeadFieldsEof(head,fields,bin);
                    break;
                default:
                    this.addField(bin);
                    break;
            }
            return;
        }

        /**
         * 处理行和结束
         */
        if(status == status_rows_eof){
            switch (bin.body[0]){
                case (byte) 0xff:
                    this.triggerError(bin);
                    break;
                case (byte)0xfe:
                    this.triggerRowsEof(bin);
                    break;
                default:
                    this.triggerRow(bin);
            }
            return;
        }
    }


    void initFieldsSize(BinaryPacket bin){
        try{
            lock.lock();
            //下一步异步处理fields和eof
            status = status_fields_eof;
            //头信息
            head = bin;
            //初始化, // TODO: 16/12/22
            fields = new ArrayList<>();
        }finally {
            lock.unlock();
        }

    }

    void addField(BinaryPacket bin){
        try {
            lock.lock();
            fields.add(bin);
        }finally {
            lock.unlock();
        }

    }


    void triggerSuccess(BinaryPacket bin){
        try {
            lock.lock();
            status = status_init_and_head;
            if(trigger !=null){
                trigger.onSuccess(bin);
            }
        }finally {

            lock.unlock();
        }

    }

    void triggerError(BinaryPacket bin){
        try {
            lock.lock();
            status = status_init_and_head;
            if(trigger != null){
                trigger.onError(bin);
            }

        }finally {
            lock.unlock();
        }

    }

    void triggerHeadFieldsEof(BinaryPacket head, List<BinaryPacket> fields,BinaryPacket eof){
        try {
            lock.lock();
            status = status_rows_eof;
            if(trigger !=null){
                trigger.onHeadFieldsEof(head,fields,eof);
            }
        }finally {
            lock.unlock();
        }

    }

    void triggerRow(BinaryPacket row){
        try {
            lock.lock();
            if(trigger !=null){
                trigger.onRow(row);
            }
        }finally {
            lock.unlock();
        }

    }

    void triggerRowsEof(BinaryPacket bin){
        try {
            status = status_init_and_head;
            lock.lock();
            if(trigger !=null){
                trigger.onRowsEof(bin);
            }
        }finally {
            lock.unlock();
        }

    }

    public synchronized void setTrigger(Trigger trigger){
        this.trigger =  trigger;
        status = status_init_and_head;
    }

    public synchronized void clearTrigger(){
        this.trigger =  null;
        status = status_init_and_head;
    }
}
